<?php

namespace app\admin\service\dev;

use app\admin\model\dev\Action as ActionModel;
use app\admin\validate\dev\Action as ActionValidate;
use app\admin\model\admin\I18n as I18nModel;
use app\admin\model\dev\Prop as PropModel;
use app\admin\service\admin\I18nService;
use app\api\service\common\AuthCheckService;
use app\common\exception\BusinessException;
use app\common\facade\StrHelper;
use think\facade\Db;
use think\facade\Log;
use think\facade\Config;
use app\admin\service\common\SqlProcessService;
use think\helper\Str;

class ActionService
{

    /**
     * 获得方法语言键
     * @param $action
     * @param $menu_id
     * @return string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function getActionLangKey($action, $menu_id): string
    {
        $key = '';
        $inBuildActions = [
            'index', 'modify', 'add', 'update', 'detail', 'delete', 'drag', 'setting', 'increase',
            'decrease', 'export', 'import', 'reset_pwd', 'to_tag', 'to_modal', 'print_list', 'customize',
        ];
        if (in_array($action, $inBuildActions)) {
            $key = 'common.button.' . $action;
        } else {
            $menuInfo = Db::name('dev_menu')->where('id', $menu_id)->find();
            $menu_names = StrHelper::getMenuNames($menuInfo['controller']);
            if ($menuInfo['key']) {
                $key = $menu_names['action_lang_prefix'] . $action;
            }
        }
        return $key;
    }

    /**
     * 仅允许创建一个方法的类型
     * @return bool
     */
    public static function checkActionType($type, $menu_id): bool
    {
        $count = 0;
        if (in_array($type, ['modify', 'index', 'drag'])) {
            $count = ActionModel::where('type', $type)->where('menu_id', $menu_id)->count();
        }
        return $count !== 0;
    }

    /**
     * 获得SQL数据源字段及SQL的列表
     * @param $menu_info
     * @return array
     */
    public static function getSqlOptionsList($menu_info): array
    {
        if (!$menu_info) {
            trace("尝试获取不存在的菜单SQL选项列表", ['menuName' => $menu_info['route_name']]);
            return []; // 如果菜单不存在，返回空数组，防止后续代码出错
        }
        $prefix = Config::get("database.connections.{$menu_info['connect']}.prefix");
        $sqlList = ActionModel::field('method,config')
            ->where('menu_id', $menu_info['id'])
            ->whereNotNull('config->index->filter')
            ->select()->toArray();
        $result = [];
        foreach ($sqlList as $item) {
            $filter = explode('|', $item['config']['index']['filter']);
            if (!empty($filter) && count($filter) == 2) {
                $result[$item['method']]['sql'] = (new SqlProcessService())->addTablePrefix(trim($filter[1]), $prefix);
                $result[$item['method']]['is_permission'] = isset($item['config']['index']['is_permission']) && $item['config']['index']['is_permission'] == 1;
            }
        }
        return $result;
    }


    public static function defaultConfig(): array
    {
        $list = Db::name('dev_prop')
            ->field('value,config')
            ->json(['config'])
            ->where('type', '=', 'action')
            ->where('status', '=', 1)
            ->column('config', 'value');
        return $list;
    }

    public static function getMenuFieldList($id): array
    {
        $fields = Db::name('dev_field')
            ->field('id, title, field')
            ->where('menu_id', $id)
            ->where('delete_time', null)
            ->where('status', 1)
            ->order('sort', 'asc')
            ->select(); // 直接获取集合对象
        $baseExcludeFields = ['create_time', 'update_time', 'delete_time', 'create_id', 'update_id', 'delete_id', 'create_dept'];
        $result = [];
        foreach ($fields as $field) {
            if (empty($field['field'])) {
                continue;
            }
            $label = !empty($field['title']) ? "{$field['field']} ({$field['title']})" : $field['field'];
            $item = ['label' => $label, 'value' => $field['field']];
            if (!in_array($field['field'], $baseExcludeFields)) {
                $result[] = $item;
            }
        }
        return $result;
    }

    public static function fieldsList($id): array
    {
        // 1. 数据库查询保持不变，但移除了 toArray()，直接使用集合
        $fields = Db::name('dev_field')
            ->field('id, title, field')
            ->where('menu_id', $id)
            ->where('delete_time', null)
            ->where('status', 1)
            ->order('sort', 'asc')
            ->select(); // 直接获取集合对象

        // 2. 定义基础的排除字段列表
        $baseExcludeFields = ['create_time', 'update_time', 'delete_time', 'create_id', 'update_id', 'delete_id', 'create_dept'];

        $resultHasId = [];
        $resultNoId = [];
        $resultFieldsHasId = [];
        $resultFieldsNoId = [];

        // 3. 一次循环，同时处理两个结果数组
        foreach ($fields as $field) {
            // 过滤空字段，这是两个结果共有的条件
            if (empty($field['field'])) {
                continue;
            }

            // 构造 label，逻辑更清晰
            $label = !empty($field['title']) ? "{$field['field']} ({$field['title']})" : $field['field'];
            $items_id = ['label' => $label, 'value' => $field['id']];
            $items_field = ['label' => $label, 'value' => $field['field']];

            // 4. 根据字段名进行条件判断，分配到不同数组
            // 检查是否在基础排除列表之外
            if (!in_array($field['field'], $baseExcludeFields)) {
                // 'hasId' 结果集只需要满足基础排除条件
                $resultHasId[] = $items_id;
                $resultFieldsHasId[] = $items_field;

                // 'noId' 结果集还需要额外排除 'id' 字段
                if ($field['field'] !== 'id') {
                    $resultNoId[] = $items_id;
                    $resultFieldsNoId[] = $items_field;
                }
            }
        }

        return [
            'fieldHasId' => $resultFieldsHasId,
            'fieldNoId' => $resultFieldsNoId,
            'hasId' => $resultHasId,
            'noId' => $resultNoId,
        ];
    }

    public static function fieldsNaleList($id): array
    {
        // 1. 数据库查询保持不变，但移除了 toArray()，直接使用集合
        $fields = Db::name('dev_field')
            ->field('id, title, field')
            ->where('menu_id', $id)
            ->where('delete_time', null)
            ->where('status', 1)
            ->order('sort', 'asc')
            ->select(); // 直接获取集合对象

        // 2. 定义基础的排除字段列表
        $baseExcludeFields = ['create_time', 'update_time', 'delete_time', 'create_id', 'update_id', 'delete_id', 'create_dept'];

        $resultHasId = [];
        $resultNoId = [];

        // 3. 一次循环，同时处理两个结果数组
        foreach ($fields as $field) {
            // 过滤空字段，这是两个结果共有的条件
            if (empty($field['field'])) {
                continue;
            }

            // 构造 label，逻辑更清晰
            $label = !empty($field['title']) ? "{$field['field']} ({$field['title']})" : $field['field'];
            $item = ['label' => $label, 'value' => $field['field']];

            // 4. 根据字段名进行条件判断，分配到不同数组
            // 检查是否在基础排除列表之外
            if (!in_array($field['field'], $baseExcludeFields)) {
                // 'hasId' 结果集只需要满足基础排除条件
                $resultHasId[] = $item;

                // 'noId' 结果集还需要额外排除 'id' 字段
                if ($field['field'] !== 'id') {
                    $resultNoId[] = $item;
                }
            }
        }

        return [
            'hasId' => $resultHasId,
            'noId' => $resultNoId,
        ];
    }

    /**
     * 添加
     * @param $post
     * @return void
     * @throws \Exception
     */
    public static function addAction($post): void
    {
        try {
            // 开启事务
            Db::startTrans();
            if (ActionService::checkActionType($post['type'], $post['menu_id'])) {
                throw new BusinessException('此类型不允许创建多个方法');
            }
            ['data' => $data, 'menu_info' => $menu_info] = self::transferData($post);
            // 确定排序号是使用提交的，还是使用计算的
            $maxSort = ActionModel::where('menu_id', $data['menu_id'])->max('sort');
            $data['sort'] = $data['sort'] ?: $maxSort + 1;

            // api 接口添加的方法默认是关闭
            if ($menu_info['app'] === 'api') {
                $data['auth_jwt'] = 0;
                $data['auth_sms'] = 0;
                $data['auth_img'] = 0;
            }
            // 增加方法信息
            ActionModel::create($data);
            // 更新翻译数据
            if ($menu_info['app'] === 'admin') {
                I18nService::asyncTranslationData($data['key'], $data['translations']);
            } else {
                app(AuthCheckService::class)->clearCache();
            }
            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 更新
     * @param $post
     */
    public static function updateAction($post): void
    {
        try {
            // 开启事务
            Db::startTrans();
            ['data' => $data, 'menu_info' => $menu_info] = self::transferData($post);
            ActionModel::update($data);
            if ($menu_info['app'] === 'admin') {
                // 更新翻译数据
                I18nService::asyncTranslationData($data['key'], $data['translations']);
            } else {
                app(AuthCheckService::class)->clearCache();
            }
            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            throw $e;
        }
    }

    /**
     * 转换数据，同步更新选项卡信息
     * @param $post
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private static function transferData($post): array
    {
        // 获得当前方法所属菜单信息
        $menu_info = Db::name('dev_menu')->find($post['menu_id']);
        $menu_names = StrHelper::getMenuNames($menu_info['controller']);
        $post['buildin'] = $post['type'] == $post['method'] ? 1 : 0;
        $post['url'] = $menu_names['url_prefix'] . '/' . Str::camel($post['method']);
        if ($menu_info['app'] === 'admin') {
            $post['code'] = $menu_names['action_code_prefix'] . $post['method'];
            $post['auth_jwt'] = null;
            $post['auth_sms'] = null;
            $post['auth_img'] = null;
            $post['app'] = 'admin';
        } else {
            $post['code'] = null;
            $post['auth_jwt'] = 0;
            $post['auth_sms'] = 0;
            $post['auth_img'] = 0;
            $post['app'] = 'api';
        }

        if (
            self::hasTabs($post) ||  // 配置了选项卡
            !empty($configData['fields']['fields']) || // 配置了字段
            !empty($configData['fields']['span']) // 配置了布局列
        ) {
            $types = ['add', 'update', 'detail'];
            $actionList = ActionModel::where('menu_id', $post['menu_id'])
                ->whereIn('type', $types)
                ->where('id', '!=', $post['id'])
                ->select();
            foreach ($actionList as $action) {
                $configData = $action->config;
                if (!self::hasTabs($action)) {
                    $configData['fields']['tab_config'] = $post['config']['fields']['tab_config'];
                }
                if (empty($configData['fields']['fields'])) {
                    $configData['fields']['fields'] = $post['config']['fields']['fields'];
                }
                if (empty($configData['fields']['span'])) {
                    $configData['fields']['span'] = $post['config']['fields']['span'];
                }
                $action->config = $configData;
                ActionModel::update($action);
            }
        }
        return ['data' => $post, 'menu_info' => $menu_info];
    }

    private static function hasTabs($data)
    {
        $result = false;
        if (
            $data['config']['fields']['tab_config'] &&
            $data['config']['fields']['tab_config'][0]['tab_name'] &&
            $data['config']['fields']['tab_config'][0]['tab_fields'] &&
            count($data['config']['fields']['tab_config'][0]['tab_fields']) > 0
        ) {
            $result = true;
        }
        if (
            empty($data['config']['fields']['tab_config']) ||
            empty($data['config']['fields']['tab_config'][0]['tab_name']) ||
            empty($data['config']['fields']['tab_config'][0]['tab_fields']) ||
            count($data['config']['fields']['tab_config'][0]['tab_fields']) == 0
        ) {
            $result = false;
        }
        return $result;
    }


    /**
     * 根据菜单变化删除方法
     * @param $menu_id
     * @return void
     * @throws \Exception
     */
    public static function deleteAction($menu_id): void
    {
        // 一次性查询出所有需要处理的字段信息
        $all_actions = ActionModel::where('menu_id', $menu_id)->select()->toArray();

        // 如果该菜单下没有任何字段，直接返回，避免空操作
        if (empty($all_actions)) {
            return;
        }

        // 在内存中准备好需要操作的数据
        $action_ids_to_delete = array_column($all_actions, 'id');

        $lang_keys_to_delete = array_column(
            array_filter($all_actions, fn($field) => $field['buildin'] != 1),
            'key'
        );

        try {
            // 开启事务
            Db::startTrans();

            // 删除字段元数据
            if (!empty($action_ids_to_delete)) {
                ActionModel::destroy($action_ids_to_delete);
            }

            // 3.3. 删除对应的多语言键
            if (!empty($lang_keys_to_delete)) {
                I18nModel::whereIn('key', $lang_keys_to_delete)->delete();
            }

            // 删除api接口白名单缓存
            app(AuthCheckService::class)->clearCache();

            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            Log::error('delete field error' . $e->getMessage());
            throw $e;
        }
    }

    /**
     * 批量删除字段
     * @param $ids
     * @return void
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function deleteHandle($ids): void
    {
        // 一次性查询出所有需要处理的字段信息
        $all_actions = ActionModel::whereIn('id', $ids)->select()->toArray();

        // 如果该菜单下没有任何字段，直接返回，避免空操作
        if (empty($all_actions)) {
            return;
        }

        // 在内存中准备好需要操作的数据
        $action_ids_to_delete = array_column($all_actions, 'id');

        $lang_keys_to_delete = array_column(
            array_filter($all_actions, fn($field) => $field['buildin'] != 1),
            'key'
        );

        try {
            // 开启事务
            Db::startTrans();

            // 删除字段元数据
            if (!empty($action_ids_to_delete)) {
                ActionModel::destroy($action_ids_to_delete);
            }

            // 3.3. 删除对应的多语言键
            if (!empty($lang_keys_to_delete)) {
                I18nModel::whereIn('key', $lang_keys_to_delete)->delete();
            }

            // 删除api接口白名单缓存
            app(AuthCheckService::class)->clearCache();

            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回退事务
            Db::rollback();
            Log::error('delete field error' . $e->getMessage());
            throw $e;
        }
    }
}